﻿using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using Microsoft.Win32;
using Microsoft.WindowsAPICodePack.Dialogs;
using MinecraftGPS.Data;

namespace MinecraftGPS
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    partial class MainWindow : Window
    {
        private DispatcherTimer timer;
        private ObservableCollection<Bookmark> bookmarks;
        private Coordinates relativePos;

        public MainWindow()
        {
            InitializeComponent();

            this.closeButton.Click += (sender, e) =>
            {
                this.Close();
            };

            this.dragButton.PreviewMouseLeftButtonDown += (sender, e) =>
            {
                this.DragMove();
            };

            this.Title += " " + this.GetType().Assembly.GetName().Version.ToString();

            bookmarks = new ObservableCollection<Bookmark>();
            bookmarkListBox.ItemsSource = bookmarks;
            bookmarkListBox.DisplayMemberPath = "Name";

            compactMenuItem.Click += CompactMenuItem_Click;
            standardMenuItem.Click += StandardMenuItem_Click;

            relativeCoordinatesMenuItem.Click += RelativeCoordinatesMenuItem_Click;
            compassCoordinatesMenuItem.Click += CompassCoordinatesMenuItem_Click;

            world1MenuItem.Click += LoadWorld(1);
            world2MenuItem.Click += LoadWorld(2);
            world3MenuItem.Click += LoadWorld(3);
            world4MenuItem.Click += LoadWorld(4);
            world5MenuItem.Click += LoadWorld(5);

            otherWorldMenuItem.Click += OtherWorldMenuItem_Click;

            opacity100MenuItem.Click += SetOpacity(1.0);
            opacity75MenuItem.Click += SetOpacity(0.75);
            opacity50MenuItem.Click += SetOpacity(0.50);
            opacity25MenuItem.Click += SetOpacity(0.25);

            bookmarkListBox.SelectionChanged += BookmarkListBox_SelectionChanged;

            addBookmark.Click += AddBookmark_Click;
            deleteBookmark.Click += DeleteBookmark_Click;
            editBookmark.Click += EditBookmark_Click;

            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(500);
            timer.Tick += Timer_Tick;
            timer.Start();
        }

        void CompassCoordinatesMenuItem_Click(object sender, RoutedEventArgs e)
        {
            relativeGroup.Visibility = System.Windows.Visibility.Hidden;
            compass.Visibility = System.Windows.Visibility.Visible;

            relativeCoordinatesMenuItem.IsChecked = false;
            compassCoordinatesMenuItem.IsChecked = true;
        }

        void RelativeCoordinatesMenuItem_Click(object sender, RoutedEventArgs e)
        {
            relativeGroup.Visibility = System.Windows.Visibility.Visible;
            compass.Visibility = System.Windows.Visibility.Hidden;

            relativeCoordinatesMenuItem.IsChecked = true;
            compassCoordinatesMenuItem.IsChecked = false;
        }

        private World currentWorld;
        private World CurrentWorld
        {
            get { return currentWorld; }
            set
            {
                if (currentWorld == value)
                    return;

                if (currentWorld != null)
                    Bookmark.Save(currentWorld, bookmarks);

                currentWorld = value;

                addBookmark.IsEnabled = currentWorld != null;

                bookmarks.Clear();

                if (currentWorld != null)
                {
                    var loadedBookmarks = Bookmark.Load(currentWorld);
                    foreach (var bookmark in loadedBookmarks)
                        bookmarks.Add(bookmark);
                }

                bookmarkListBox.SelectedIndex = -1;
            }
        }

        private void ClearPositionData()
        {
            this.posX.Content = "";
            this.posY.Content = "";
            this.posZ.Content = "";

            this.chunkX.Content = "";
            this.chunkZ.Content = "";

            this.relativeX.Content = "";
            this.relativeY.Content = "";
            this.relativeZ.Content = "";
        }

        void Timer_Tick(object sender, EventArgs e)
        {
            if (CurrentWorld == null)
            {
                status.Content = "No World Selected";

                ClearPositionData();

                timer.Stop();
            }
            else
            {
                PlayerInfo player;

                try
                {
                    player = CurrentWorld.GetPlayerInfo();
                }
                catch
                {
                    player = null;
                }

                if (player == null)
                {
                    status.Content = "Bad Data";

                    ClearPositionData();
                }
                else
                {
                    var worldPathBits = currentWorld.WorldPath.Split(Path.DirectorySeparatorChar);

                    status.Content = worldPathBits[worldPathBits.Length - 1];

                    compassRotation.Angle = 90 - player.Direction.Bearing;

                    var blockPos = new Coordinates(Math.Floor(player.Position.X), Math.Floor(player.Position.Y), Math.Floor(player.Position.Z));

                    this.posX.Content = blockPos.X.ToString("F0");
                    this.posY.Content = blockPos.Y.ToString("F0");
                    this.posZ.Content = blockPos.Z.ToString("F0");

                    var chunk = player.Position.ToChunkCoordinates();

                    this.chunkX.Content = chunk.X.ToString();
                    this.chunkZ.Content = chunk.Z.ToString();

                    if (!relativePos.IsEmpty)
                    {
                        this.relativeX.Content = (blockPos.X - relativePos.X).ToString("F0");
                        this.relativeY.Content = (blockPos.Y - relativePos.Y).ToString("F0");
                        this.relativeZ.Content = (blockPos.Z - relativePos.Z).ToString("F0");

                        var diff = new Coordinates(relativePos.X - blockPos.X, relativePos.Y - blockPos.Y, relativePos.Z - blockPos.Z);

                        double distance =
                            (blockPos.X - relativePos.X) * (blockPos.X - relativePos.X) +
                            (blockPos.Z - relativePos.Z) * (blockPos.Z - relativePos.Z);

                        distance = Math.Sqrt(distance);

                        waypoint.Visibility = distance == 0 ? Visibility.Hidden : Visibility.Visible;
                        waypointPitch.Visibility = distance == 0 && diff.Y == 0 ? Visibility.Hidden : Visibility.Visible;
                        distanceText.Foreground = distance != 0 ? Brushes.Yellow : Brushes.Red;

                        distanceText.Text = distance != 0 ? distance.ToString("F1") : Math.Abs(diff.Y).ToString("F1");

                        double angle = Math.Atan2(diff.Z, diff.X);

                        waypointRotation.Angle = 180 + angle * 180 / Math.PI + compassRotation.Angle;

                        if (distance != 0)
                        {
                            angle = Math.Atan2(relativePos.Y - blockPos.Y, Math.Sqrt(diff.X * diff.X + diff.Z * diff.Z)) * 180 / Math.PI;
                            waypointPitchTranslation.Y = Math.Max(Math.Min((angle - player.Direction.Pitch) / 2, 45), -45);
                        }
                        else
                        {
                            waypointPitchTranslation.Y = diff.Y > 0 ? -45 : diff.Y < 0 ? 45 : 0;
                        }
                    }
                    else
                    {
                        this.relativeX.Content = "";
                        this.relativeY.Content = "";
                        this.relativeZ.Content = "";
                    }
                }
            }
        }

        protected override void OnClosed(System.EventArgs e)
        {
            CurrentWorld = null;
            base.OnClosed(e);
        }

        void StandardMenuItem_Click(object sender, RoutedEventArgs e)
        {
            standardMenuItem.IsChecked = true;
            compactMenuItem.IsChecked = false;

            relativeGrid.Visibility = Visibility.Visible;
            bookmarkListBox.Visibility = Visibility.Visible;
            bookmarkButtons.Visibility = Visibility.Visible;
        }

        void CompactMenuItem_Click(object sender, RoutedEventArgs e)
        {
            standardMenuItem.IsChecked = false;
            compactMenuItem.IsChecked = true;

            relativeGrid.Visibility = Visibility.Collapsed;
            bookmarkListBox.Visibility = Visibility.Collapsed;
            bookmarkButtons.Visibility = Visibility.Collapsed;
        }

        void OtherWorldMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (CommonFileDialog.IsPlatformSupported)
            {
                CommonOpenFileDialog cofd = new CommonOpenFileDialog();
                cofd.Filters.Add(new CommonFileDialogFilter("World Data File", "dat"));

                if (cofd.ShowDialog() == CommonFileDialogResult.Ok)
                {
                    CurrentWorld = new World(Path.GetDirectoryName(cofd.FileName));

                    timer.Start();
                }
            }
            else
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter = "World Data File|*.dat";

                if (ofd.ShowDialog() == true)
                {
                    CurrentWorld = new World(Path.GetDirectoryName(ofd.FileName));

                    timer.Start();
                }
            }
        }

        private RoutedEventHandler LoadWorld(int i)
        {
            return delegate(object sender, RoutedEventArgs e)
            {
                CurrentWorld = World.LoadSave(i);

                timer.Start();
            };
        }

        private RoutedEventHandler SetOpacity(double opacity)
        {
            return delegate(object sender, RoutedEventArgs e)
            {
                this.Opacity = opacity;

                opacity100MenuItem.IsChecked = false;
                opacity75MenuItem.IsChecked = false;
                opacity50MenuItem.IsChecked = false;
                opacity25MenuItem.IsChecked = false;

                ((MenuItem)sender).IsChecked = true;
            };
        }

        void BookmarkListBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            deleteBookmark.IsEnabled = bookmarkListBox.SelectedIndex != -1;
            editBookmark.IsEnabled = bookmarkListBox.SelectedIndex != -1;

            if (bookmarkListBox.SelectedIndex != -1)
            {
                var bookmark = bookmarks[bookmarkListBox.SelectedIndex];
                relativePos = bookmark.Position;
                waypoint.Visibility = System.Windows.Visibility.Visible;
                distanceBorder.Visibility = System.Windows.Visibility.Visible;
                waypointPitch.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                relativePos = new Coordinates();
                waypoint.Visibility = System.Windows.Visibility.Hidden;
                distanceBorder.Visibility = System.Windows.Visibility.Hidden;
                waypointPitch.Visibility = System.Windows.Visibility.Hidden;
            }
        }

        void AddBookmark_Click(object sender, RoutedEventArgs e)
        {
            BookmarkWindow bookmarkWindow = new BookmarkWindow();
            bookmarkWindow.Owner = this;
            bookmarkWindow.BookmarkName = "New Bookmark";

            if (bookmarkWindow.ShowDialog() == true)
            {
                int x = Convert.ToInt32(posX.Content);
                int y = Convert.ToInt32(posY.Content);
                int z = Convert.ToInt32(posZ.Content);

                bookmarks.Add(new Bookmark(bookmarkWindow.BookmarkName, new Coordinates(x, y, z)));

                Bookmark.Save(currentWorld, bookmarks);

                bookmarkListBox.SelectedIndex = -1;
            }
        }

        void DeleteBookmark_Click(object sender, RoutedEventArgs e)
        {
            bookmarks.RemoveAt(bookmarkListBox.SelectedIndex);

            Bookmark.Save(currentWorld, bookmarks);
        }

        void EditBookmark_Click(object sender, RoutedEventArgs e)
        {
            BookmarkWindow bookmarkWindow = new BookmarkWindow();
            bookmarkWindow.Owner = this;
            bookmarkWindow.BookmarkName = bookmarks[bookmarkListBox.SelectedIndex].Name;

            if (bookmarkWindow.ShowDialog() == true)
            {
                bookmarks[bookmarkListBox.SelectedIndex].Name = bookmarkWindow.BookmarkName;

                Bookmark.Save(currentWorld, bookmarks);
            }
        }
    }
}
